/*
* nvg is a C++ wrapper for NanoVG.
*
* @author: dzlua
* @contact: 505544956@qq.com
* @date: October 27, 2020
* @example: https://github.com/memononen/nanovg.git
*/

#ifndef __NVG_HPP_
#define __NVG_HPP_

#include <nanovg.h>

namespace nvg {
  
// Warpper a C style class to c++ class.
// https://gitee.com/dzlua/test/blob/master/WrapperC/wrapper.hpp
template <typename T, typename C>
class Wrapper {
  public:
    Wrapper(C *cptr = nullptr) noexcept { reset(cptr); }
    Wrapper(const Wrapper<T,C>& r) noexcept { reset(r.get()); }
    Wrapper<T,C>& operator=(const Wrapper<T,C>& r) noexcept { reset(r.get()); return *this; }
    Wrapper<T,C>& operator=(C *cptr) noexcept { reset(cptr); return *this; }
  public:
    inline T* operator->() noexcept { return &obj_; }
    inline operator C*() const noexcept { return get(); }
    inline explicit operator bool() const noexcept { return get() != nullptr; }
  public:
    inline C* get() const noexcept { return obj_._get(); }
    inline C*& set() noexcept { return obj_._set(); }
    inline void reset(C *cptr = nullptr) noexcept { obj_._reset(cptr); }
    inline void swap(Wrapper<T,C>& r) noexcept { obj_._swap(r.obj_); }
  protected:
    T obj_;
};

template <typename T, typename C>
inline bool operator==(const Wrapper<T,C>& l, const Wrapper<T,C>& r) noexcept { return l.get() == r.get(); }
template <typename T, typename C>
inline bool operator==(const Wrapper<T,C>& l, std::nullptr_t) noexcept { return l.get() == nullptr; }
template <typename T, typename C>
inline bool operator==(std::nullptr_t, const Wrapper<T,C>& r) noexcept { return nullptr == r.get(); }
template <typename T, typename C>
inline bool operator!=(const Wrapper<T,C>& l, const Wrapper<T,C>& r) noexcept { return l.get() != r.get(); }
template <typename T, typename C>
inline bool operator!=(const Wrapper<T,C>& l, std::nullptr_t) noexcept { return l.get() != nullptr; }
template <typename T, typename C>
inline bool operator!=(std::nullptr_t, const Wrapper<T,C>& r) noexcept { return nullptr != r.get(); }

template <typename T, typename C>
class ClassWrapped {
    friend class Wrapper<T,C>;
  protected:
    ClassWrapped() : cptr_(nullptr) {}
    virtual ~ClassWrapped() { _reset(nullptr); }
    inline C* _get() const noexcept { return cptr_; }
    inline C*& _set() noexcept { return cptr_; }
    inline void _reset(C *cptr = nullptr) noexcept { cptr_ = cptr; }
    inline void _swap(ClassWrapped<T,C>& r) noexcept { std::swap(cptr_, r.cptr_); }
  protected:
    C* cptr_;
};

// macros for wrapper.
#define WRAPPER_CLASS_BEGIN(classname,structname) \
  class classname : public ClassWrapped<classname, structname> { \
      friend class Wrapper<classname,structname>; \
    protected: \
      classname() = default; \
    public: \
      typedef Wrapper<classname,structname> Wrapped;
#define WRAPPER_CLASS_END };

// base
enum class LineCap {
  Butt = NVG_BUTT,
  Round = NVG_ROUND,
  Square = NVG_SQUARE
};

enum class LineJoin {
  Miter = NVG_MITER,
  Round = NVG_ROUND,
  Bevel = NVG_BEVEL
};

enum class Winding {
  Solid = NVG_SOLID,  // Winding for solid shapes
  Hole = NVG_HOLE     // Winding for holes
};

enum class Direction {
  CounterClockwise = NVG_CCW,  // Winding for solid shapes
  Clockwise = NVG_CW           // Winding for holes
};

// Horizontal align
enum class HorAlign {
  Left = NVG_ALIGN_LEFT,          // Default, align text horizontally to left.
  Center = NVG_ALIGN_CENTER,      // Align text horizontally to center.
  Right = NVG_ALIGN_RIGHT         // Align text horizontally to right.
};

// Vertical align
enum class VerAlign {
  BaseLine = NVG_ALIGN_BASELINE,  // Default, align text vertically to baseline.
  Top = NVG_ALIGN_TOP,            // Align text vertically to top.
  Middle = NVG_ALIGN_MIDDLE,      // Align text vertically to middle.
  Bottom = NVG_ALIGN_BOTTOM       // Align text vertically to bottom.
};

struct Bounds {
  union {
		float value[4];
		struct {
      float l, t, r, b;
		};
	};
  Bounds() { clear(); }
  void clear() { l = t = r = b = 0.f; }
};

struct Color {
  NVGcolor color;
  inline operator NVGcolor&() noexcept { return color; }

  // Returns a color value from red, green, blue and alpha values.
  static inline Color fromRGBA(float r, float g, float b, float a = 1.f) {
    Color c; c.setRGBA(r, g, b, a); return std::move(c); }
  // Returns a color value from red, green, blue and alpha values.
  static inline Color fromRGBAu(unsigned char r, unsigned char g, unsigned char b, unsigned char a = 255) {
    Color c; c.setRGBAu(r, g, b, a); return std::move(c); }
  // Returns color value specified by hue, saturation and lightness and alpha.
  static inline Color fromHSLA(float h, float s, float l, float a = 1.f) {
    Color c; c.setHSLA(h, s, l, a); return std::move(c); }
  // Returns color value specified by hue, saturation and lightness and alpha.
  static inline Color fromHSLAu(float h, float s, float l, unsigned char a = 255) {
    Color c; c.setHSLAu(h, s, l, a); return std::move(c); }

  // Linearly interpolates from color to c, and returns resulting color value.
  inline void lerpRGBA(NVGcolor c, float u) { color = nvgLerpRGBA(color, c, u); }

  // Sets transparency of a color value.
  inline void transRGBA(float a) { color.a = a; }
  // Sets transparency of a color value.
  inline void transRGBAu(unsigned char a) { color.a = a / 255.f; }

  // Set a color value from red, green, blue and alpha values.
  inline void setRGBA(float r, float g, float b, float a = 1.f) {
    color.a = a; color.r = r; color.g = g; color.b = b; }
  // Set a color value from red, green, blue and alpha values.
  inline void setRGBAu(unsigned char r, unsigned char g, unsigned char b, unsigned char a = 255) {
    color.a = a / 255.f; color.r = r / 255.f; color.g = g / 255.f; color.b = b / 255.f; }
  // Set color value specified by hue, saturation and lightness and alpha.
  inline void setHSLA(float h, float s, float l, float a = 1.f) {
    color = nvgHSLA(h, s, l, (int)(a * 255)); }
  // Set color value specified by hue, saturation and lightness and alpha.
  inline void setHSLAu(float h, float s, float l, unsigned char a = 255) {
    color = nvgHSLA(h, s, l, a); }
};

struct Matrix {
  union {
		float value[6];
		struct {
      float a, b, c, d, e, f;
		};
	};
  Matrix() { identity(); }
  // Sets the transform to identity matrix.
  void identity() { nvgTransformIdentity(value); }
  // Sets the transform to translation matrix matrix.
  void translate(float tx, float ty) { nvgTransformTranslate(value, tx, ty); }
  // Sets the transform to scale matrix.
  void scale(float sx, float sy) { nvgTransformScale(value, sx, sy); }
  // Sets the transform to rotate matrix. Angle is specified in radians.
  void rotate(float a) { nvgTransformRotate(value, a); }
  // Sets the transform to skew-x matrix. Angle is specified in radians.
  void skewX(float a) { nvgTransformSkewX(value, a); }
  // Sets the transform to skew-y matrix. Angle is specified in radians.
  void skewY(float a) { nvgTransformSkewY(value, a); }
  // Sets the transform to the result of multiplication of two transforms, of A = A*B.
  void multiply(const Matrix &src) { nvgTransformMultiply(value, src.value); }
  // Sets the transform to the result of multiplication of two transforms, of A = B*A.
  void premultiply(const Matrix &src) { nvgTransformPremultiply(value, src.value); }
  // Sets the destination to inverse of specified transform.
  bool inverse(const Matrix &src) { return nvgTransformInverse(value, src.value); }
  // Transform a point by given transform.
  void transPoint(float* dstx, float* dsty, float srcx, float srcy) {
    nvgTransformPoint(dstx, dsty, value, srcx, srcy); }
};

namespace detail {
  WRAPPER_CLASS_BEGIN(Font, NVGcontext)
    friend class Canvas;
    inline operator int() const noexcept { return font_; }

    inline bool isOK() { return font_ >= 0; }

    // Adds a fallback font by handle.
    inline bool addFallback(const Font &font) {
      return nvgAddFallbackFontId(cptr_, font_, font); }
    // Resets fallback fonts by name.
    inline void resetFallback() { nvgResetFallbackFontsId(cptr_, font_); }
  protected:
    int font_;
  WRAPPER_CLASS_END

  //
  // Images
  //
  // NanoVG allows you to load jpg, png, psd, tga, pic and gif files to be used for rendering.
  // In addition you can upload your own image. The image loading is provided by stb_image.
  // The parameter imageFlags is combination of flags defined in NVGimageFlags.
  WRAPPER_CLASS_BEGIN(Image, NVGcontext)
    friend class Canvas;
    inline operator int() const noexcept { return img_; }

    inline bool isOK() { return img_ != 0; }

    // Updates image data specified by image handle.
    inline void update(const unsigned char* data) {
      nvgUpdateImage(cptr_, img_, data); }
    // Returns the dimensions of a created image.
    inline void size(int* w, int* h) {
      nvgImageSize(cptr_, img_, w, h); }
    // Deletes created image.
    inline void destroy() { nvgDeleteImage(cptr_, img_); }
  protected:
    int img_;
  WRAPPER_CLASS_END

  WRAPPER_CLASS_BEGIN(Path, NVGcontext)
    // Starts new sub-path with specified point as first point.
    inline void moveTo(float x, float y) { nvgMoveTo(cptr_, x, y); }
    // Adds line segment from the last point in the path to the specified point.
    inline void lineTo(float x, float y) { nvgLineTo(cptr_, x, y); }
    // Adds cubic bezier segment from last point in the path via two control points to the specified point.
    inline void bezierTo(float c1x, float c1y, float c2x, float c2y, float x, float y) {
      nvgBezierTo(cptr_, c1x, c1y, c2x, c2y, x, y); }
    // Adds quadratic bezier segment from last point in the path via a control point to the specified point.
    inline void quadTo(float cx, float cy, float x, float y) {
      nvgQuadTo(cptr_, cx, cy, x, y); }
    // Adds an arc segment at the corner defined by the last path point, and two specified points.
    inline void arcTo(float x1, float y1, float x2, float y2, float radius) {
      nvgArcTo(cptr_, x1, y1, x2, y2, radius); }

    // Closes current sub-path with a line segment.
    inline void close() { nvgClosePath(cptr_); }
    // Sets the current sub-path winding, see NVGwinding and NVGsolidity.
    inline void winding(Winding winding) { nvgPathWinding(cptr_, (int)winding); }

    // Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r,
    // and the arc is drawn from angle a0 to a1, and swept in direction dir (NVG_CCW, or NVG_CW).
    // Angles are specified in radians.
    inline void arc(float cx, float cy, float radius, float a0, float a1, Direction dir) {
      nvgArc(cptr_, cx, cy, radius, a0, a1, (int)dir); }
    // Creates new rectangle shaped sub-path.
    inline void rect(float x, float y, float w, float h) {
      nvgRect(cptr_, x, y, w, h); }
    // Creates new rounded rectangle shaped sub-path.
    inline void roundedRect(float x, float y, float w, float h, float radius) {
      nvgRoundedRect(cptr_, x, y, w, h, radius); }
    // Creates new rounded rectangle shaped sub-path with varying radii for each corner.
    inline void roundedRectVarying(float x, float y, float w, float h
        , float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft) {
      nvgRoundedRectVarying(cptr_, x, y, w, h
        , radTopLeft, radTopRight, radBottomRight, radBottomLeft); }
    // Creates new ellipse shaped sub-path.
    inline void ellipse(float cx, float cy, float rx, float ry) {
      nvgEllipse(cptr_, cx, cy, rx, ry); }
    // Creates new circle shaped sub-path.
    inline void circle(float cx, float cy, float radius) {
      nvgCircle(cptr_, cx, cy, radius); }
  WRAPPER_CLASS_END

  WRAPPER_CLASS_BEGIN(Canvas, NVGcontext)
    // Begin drawing a new frame
    // Calls to nanovg drawing API should be wrapped in nvgBeginFrame() & nvgEndFrame()
    // nvgBeginFrame() defines the size of the window to render to in relation currently
    // set viewport (i.e. glViewport on GL backends). Device pixel ration allows to
    // control the rendering on Hi-DPI devices.
    // For example, GLFW returns two dimension for an opened window: window size and
    // frame buffer size. In that case you would set windowWidth/Height to the window size
    // devicePixelRatio to: frameBufferWidth / windowWidth.
    inline void beginFrame(float windowWidth, float windowHeight, float devicePixelRatio) {
      nvgBeginFrame(cptr_, windowWidth, windowHeight, devicePixelRatio); }
    // Cancels drawing the current frame.
    inline void cancelFrame() { nvgCancelFrame(cptr_); }
    // Ends drawing flushing remaining render state.
    inline void endFrame() { nvgEndFrame(cptr_); }

    // Sets the composite operation. The op parameter should be one of NVGcompositeOperation.
    inline void globalCompositeOperation(int op) {
      nvgGlobalCompositeOperation(cptr_, op); }
    // Sets the composite operation with custom pixel arithmetic. The parameters should be one of NVGblendFactor.
    inline void globalCompositeBlendFunc(int sfactor, int dfactor) {
      nvgGlobalCompositeBlendFunc(cptr_, sfactor, dfactor); }
    // Sets the composite operation with custom pixel arithmetic for RGB and alpha components separately. The parameters should be one of NVGblendFactor.
    inline void globalCompositeBlendFuncSeparate(int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) {
      nvgGlobalCompositeBlendFuncSeparate(cptr_, srcRGB, dstRGB, srcAlpha, dstAlpha); }

    // Pushes and saves the current render state into a state stack.
    // A matching nvgRestore() must be used to restore the state.
    inline void save() { nvgSave(cptr_); }
    // Pops and restores current render state.
    inline void restore() { nvgRestore(cptr_); }
    // Resets current render state to default values. Does not affect the render state stack.
    inline void reset() { nvgReset(cptr_); }

    // Sets the transparency applied to all rendered shapes.
    // Already transparent paths will get proportionally more transparent as well.
    inline void globalAlpha(float alpha) { nvgGlobalAlpha(cptr_, alpha); }
    // Sets whether to draw antialias for nvgStroke() and nvgFill(). It's enabled by default.
    inline void shapeAntiAlias(bool enabled) { nvgShapeAntiAlias(cptr_, enabled); }
    // Sets current fill style to a solid color.
    inline void fillColor(const NVGcolor &color) { nvgFillColor(cptr_, color); }
    // Sets current fill style to a paint, which can be a one of the gradients or a pattern.
    inline void fillPaint(const NVGpaint &paint) { nvgFillPaint(cptr_, paint); }
    // Sets current stroke style to a solid color.
    inline void strokeColor(const NVGcolor &color) { nvgStrokeColor(cptr_, color); }
    // Sets current stroke style to a paint, which can be a one of the gradients or a pattern.
    inline void strokePaint(const NVGpaint &paint) { nvgStrokePaint(cptr_, paint); }
    // Sets the stroke width of the stroke style.
    inline void strokeWidth(float size) { nvgStrokeWidth(cptr_, size); }
    // Sets the miter limit of the stroke style.
    // Miter limit controls when a sharp corner is beveled.
    inline void miterLimit(float limit) { nvgMiterLimit(cptr_, limit); }
    // Sets how the end of the line (cap) is drawn
    inline void lineCap(LineCap cap) { nvgLineCap(cptr_, (int)cap); }
    // Sets how sharp path corners are drawn.
    inline void lineJoin(LineJoin join) { nvgLineJoin(cptr_, (int)join); }

    // Get current transform.
    inline Matrix currentTransform() {
      Matrix m; nvgCurrentTransform(cptr_, m.value); return std::move(m); }
    // Resets current transform to a identity matrix.
    inline void resetTransform() { nvgResetTransform(cptr_); }
    // Set current transform to a m matrix.
    inline void setTransform(const Matrix &m) {
      nvgTransform(cptr_, m.a, m.b, m.c, m.d, m.e, m.f); }
    // Translates current coordinate system.
    inline void translate(float x, float y) { nvgTranslate(cptr_, x, y); }
    // Rotates current coordinate system. Angle is specified in radians.
    inline void rotate(float angle) { nvgRotate(cptr_, angle); }
    // Skews the current coordinate system along X axis. Angle is specified in radians.
    inline void skewX(float angle) { nvgSkewX(cptr_, angle); }
    // Skews the current coordinate system along Y axis. Angle is specified in radians.
    inline void skewY(float angle) { nvgSkewY(cptr_, angle); }
    // Scales the current coordinate system.
    inline void scale(float x, float y) { nvgScale(cptr_, x, y); }

    // Sets the current scissor rectangle.
    // The scissor rectangle is transformed by the current transform.
    inline void scissor(float x, float y, float w, float h) { nvgScissor(cptr_, x, y, w, h); }
    // Intersects current scissor rectangle with the specified rectangle.
    // The scissor rectangle is transformed by the current transform.
    // Note: in case the rotation of previous scissor rect differs from
    // the current one, the intersection will be done between the specified
    // rectangle and the previous scissor rectangle transformed in the current
    // transform space. The resulting shape is always rectangle.
    inline void intersectScissor(float x, float y, float w, float h) {
      nvgIntersectScissor(cptr_, x, y, w, h); }
    // Reset and disables scissoring.
    inline void resetScissor() { nvgResetScissor(cptr_); }

    // Clears the current path and sub-paths.
    inline Path::Wrapped beginPath() {
      nvgBeginPath(cptr_); return Path::Wrapped(cptr_); }
    
    // Fills the current path with current fill style.
    inline void fill() { nvgFill(cptr_); }
    // Fills the current path with current stroke style.
    inline void stroke() { nvgStroke(cptr_); }

    // Creates image by loading it from the disk from specified file name.
    inline Image::Wrapped createImage(const char* filename, int flags) {
      auto img = Image::Wrapped(cptr_);
      img->img_ = nvgCreateImage(cptr_, filename, flags);
      return std::move(img); }
    // Creates image by loading it from the specified chunk of memory.
    inline Image::Wrapped createImageMem(int flags, unsigned char* data, int ndata) {
      auto img = Image::Wrapped(cptr_);
      img->img_ = nvgCreateImageMem(cptr_, flags, data, ndata);
      return std::move(img); }
    // Creates image from specified image data.
    inline Image::Wrapped createImageRGBA(int w, int h, int flags, const unsigned char* data) {
      auto img = Image::Wrapped(cptr_);
      img->img_ = nvgCreateImageRGBA(cptr_, w, h, flags, data);
      return std::move(img); }

    // Creates and returns a linear gradient. Parameters (sx,sy)-(ex,ey) specify the start and end coordinates
    // of the linear gradient, icol specifies the start color and ocol the end color.
    // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint().
    inline NVGpaint linearGradient(float sx, float sy, float ex, float ey,
				NVGcolor icol, NVGcolor ocol) {
      return nvgLinearGradient(cptr_, sx, sy, ex, ey, icol, ocol); }

    // Creates and returns a box gradient. Box gradient is a feathered rounded rectangle, it is useful for rendering
    // drop shadows or highlights for boxes. Parameters (x,y) define the top-left corner of the rectangle,
    // (w,h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry
    // the border of the rectangle is. Parameter icol specifies the inner color and ocol the outer color of the gradient.
    // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint().
    inline NVGpaint boxGradient(float x, float y, float w, float h,
				float r, float f, NVGcolor icol, NVGcolor ocol) {
      return nvgBoxGradient(cptr_, x, y, w, h, r, f, icol, ocol); }

    // Creates and returns a radial gradient. Parameters (cx,cy) specify the center, inr and outr specify
    // the inner and outer radius of the gradient, icol specifies the start color and ocol the end color.
    // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint().
    inline NVGpaint radialGradient(float cx, float cy, float inr, float outr,
				NVGcolor icol, NVGcolor ocol) {
      return nvgRadialGradient(cptr_, cx, cy, inr, outr, icol, ocol); }

    // Creates and returns an image patter. Parameters (ox,oy) specify the left-top location of the image pattern,
    // (ex,ey) the size of one image, angle rotation around the top-left corner, image is handle to the image to render.
    // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint().
    inline NVGpaint imagePattern(float ox, float oy, float ex, float ey,
				float angle, Image image, float alpha) {
      return nvgImagePattern(cptr_, ox, oy, ex, ey, angle, image, alpha); }

    // Creates font by loading it from the disk from specified file name.
    inline Font::Wrapped createFont(const char* name, const char* filename, const int fontIndex = 0) {
      auto font = Font::Wrapped(cptr_);
      font->font_ = nvgCreateFontAtIndex(cptr_, name, filename, fontIndex);
      return std::move(font); }
    // Creates font by loading it from the specified memory chunk.
    inline Font::Wrapped createFontMem(const char* name, unsigned char* data, int ndata, bool freeData, const int fontIndex = 0) {
      auto font = Font::Wrapped(cptr_);
      font->font_ = nvgCreateFontMemAtIndex(cptr_, name, data, ndata, freeData, fontIndex);
      return std::move(font); }

    // Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found.
    inline Font::Wrapped findFont(const char* name) {
      auto font = Font::Wrapped(cptr_);
      font->font_ = nvgFindFont(cptr_, name);
      return std::move(font); }

    // Sets the font size of current text style.
    inline void fontSize(float size) { nvgFontSize(cptr_, size); }
    // Sets the blur of current text style.
    inline void fontBlur(float blur) { nvgFontBlur(cptr_, blur); }
    // Sets the letter spacing of current text style.
    inline void textLetterSpacing(float spacing) { nvgTextLetterSpacing(cptr_, spacing); }
    // Sets the proportional line height of current text style. The line height is specified as multiple of font size.
    inline void textLineHeight(float lineHeight) { nvgTextLineHeight(cptr_, lineHeight); }

    // Sets the text align of current text style, see NVGalign for options.
    inline void rextAlign(HorAlign halign, VerAlign valign) {
      nvgTextAlign(cptr_, (int)halign | (int)valign); }
    
    // Sets the font face based on specified id of current text style.
    inline void fontFace(const Font &font) { nvgFontFaceId(cptr_, font); }
    // Sets the font face based on specified name of current text style.
    inline void fontFace(const char* font) { nvgFontFace(cptr_, font); }

    // Draws text string at specified location. If end is specified only the sub-string up to the end is drawn.
    inline float text(float x, float y, const char* string, const char* end = nullptr) {
      return nvgText(cptr_, x, y, string, end); }
    
    // Draws multi-line text string at specified location wrapped at the specified width. If end is specified only the sub-string up to the end is drawn.
    // White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered.
    // Words longer than the max width are slit at nearest character (i.e. no hyphenation).
    inline void textBox(float x, float y, float breakRowWidth, const char* string, const char* end = nullptr) {
      nvgTextBox(cptr_, x, y, breakRowWidth, string, end); }

    // Measures the specified text string. Parameter bounds should be a pointer to float[4],
    // if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax]
    // Returns the horizontal advance of the measured text (i.e. where the next character should drawn).
    // Measured values are returned in local coordinate space.
    inline float textBounds(Bounds &bounds, float x, float y, const char* string, const char* end = nullptr) {
      return nvgTextBounds(cptr_, x, y, string, end, bounds.value); }

    // Measures the specified multi-text string. Parameter bounds should be a pointer to float[4],
    // if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax]
    // Measured values are returned in local coordinate space.
    inline void textBoxBounds(Bounds &bounds, float x, float y, float breakRowWidth, const char* string, const char* end = nullptr) {
      nvgTextBoxBounds(cptr_, x, y, breakRowWidth, string, end, bounds.value); }

    // Calculates the glyph x positions of the specified text. If end is specified only the sub-string will be used.
    // Measured values are returned in local coordinate space.
    inline int textGlyphPositions(NVGglyphPosition* positions, int maxPositions, float x, float y, const char* string, const char* end = nullptr) {
      return nvgTextGlyphPositions(cptr_, x, y, string, end, positions, maxPositions); }

    // Returns the vertical metrics based on the current text style.
    // Measured values are returned in local coordinate space.
    inline void textMetrics(float* ascender, float* descender, float* lineh) {
      nvgTextMetrics(cptr_, ascender, descender, lineh); }

    // Breaks the specified text into lines. If end is specified only the sub-string will be used.
    // White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered.
    // Words longer than the max width are slit at nearest character (i.e. no hyphenation).
    inline int textBreakLines(NVGtextRow* rows, int maxRows, float breakRowWidth, const char* string, const char* end = nullptr) {
      return nvgTextBreakLines(cptr_, string, end, breakRowWidth, rows, maxRows); }
  WRAPPER_CLASS_END

} // end namespace detail
typedef detail::Font::Wrapped Font;
typedef detail::Image::Wrapped Image;
typedef detail::Path::Wrapped Path;
typedef detail::Canvas::Wrapped Canvas;

// Converts degrees to radians and vice versa.
inline float degToRad(float deg) { return nvgDegToRad(deg); }
inline float radToDeg(float rad) { return nvgRadToDeg(rad); }

} // end namespace nvg

#endif // end __NVG_HPP_